use crate::colors::COMMON_BORDER_COLOR;
use crate::components::{EditorApi, ElementHelper, NodeListHelper};
use crate::utils::dom::get_selection;
//use gloo::console::log;
use lazy_static::lazy_static;
use wasm_bindgen::JsCast;
use web_sys::{HtmlDivElement, HtmlElement, KeyboardEvent, Node, Selection, Text};
use yew::prelude::*;

lazy_static! {
    static ref EDITOR_STYLE: String =
        format!("border: 1px solid {};min-height: 100%", COMMON_BORDER_COLOR);
}

trait NodeTrait {
    fn get_inner_html(&self) -> Option<String>;
    fn get_outer_html(&self) -> Option<String>;
}

impl NodeTrait for Node {
    fn get_inner_html(&self) -> Option<String> {
        Some(self.clone().dyn_into::<HtmlElement>().ok()?.inner_html())
    }
    fn get_outer_html(&self) -> Option<String> {
        Some(self.clone().dyn_into::<HtmlElement>().ok()?.outer_html())
    }
}

pub enum Msg {
    Input,
    MouseUp,
    KeyDown(KeyboardEvent),
}
pub struct EditorCore {}

#[derive(Properties, Clone, PartialEq)]
pub struct EditorCoreProps {
    #[prop_or_default]
    pub selection_changed: Callback<Option<EditorCoreSelection>>,
}

#[derive(Default, Clone)]
pub struct EditorCoreSelection {
    pub anchor_node: Option<Node>,
    pub focus_node: Option<Node>,
    pub top_nodes: Vec<Node>,
}
impl EditorCoreSelection {
    pub fn is_focus_on_text(&self) -> bool {
        if let Some(focus_node) = &self.focus_node {
            focus_node.is_instance_of::<Text>()
        } else {
            false
        }
    }
}

impl From<Selection> for EditorCoreSelection {
    fn from(selection: Selection) -> Self {
        if selection.is_collapsed() {
            return Self {
                anchor_node: None,
                focus_node: selection.focus_node(),
                ..Self::default()
            };
        }
        let anchor_node = selection.anchor_node();
        let focus_node = selection.focus_node();

        let range = selection.get_range_at(0).unwrap();

        /*
        let start_offset = range.start_offset().unwrap();
        log!(format!("start_offset:{:?}", start_offset));

        let end_offset = range.end_offset().unwrap();
        log!(format!("end_offset:{:?}", end_offset));
        */

        let common_ancestor_container = range
            .common_ancestor_container()
            .expect("common ancestor container error");

        let node_list = common_ancestor_container.child_nodes();
        let node_len = node_list.length();
        let mut top_nodes: Vec<Node> = Vec::with_capacity(node_len as usize);
        let mut should_include = false;

        for index in 0..node_len {
            let node = node_list.item(index).unwrap();
            if should_include {
                top_nodes.push(node);
                continue;
            }
            if node.contains(anchor_node.as_ref()) {
                top_nodes.push(node);
                should_include = true;
                continue;
            }
            if node.contains(focus_node.as_ref()) {
                top_nodes.push(node);
                break;
            }
        }

        Self {
            anchor_node,
            focus_node,
            top_nodes,
        }
    }
}
impl Component for EditorCore {
    type Message = Msg;
    type Properties = EditorCoreProps;
    fn create(_ctx: &Context<Self>) -> Self {
        Self {}
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::Input => {
                if let Ok(selection) = get_selection() {
                    ctx.props().selection_changed.emit(Some(selection.into()));
                }
            }
            Msg::MouseUp => {
                if let Ok(selection) = get_selection() {
                    ctx.props().selection_changed.emit(Some(selection.into()));
                    return false;
                } else {
                    ctx.props().selection_changed.emit(None);
                    return false;
                }
            }
            Msg::KeyDown(ref event) => {
                let root_of_focus_node = EditorApi::editor_node()
                    .child_nodes()
                    .find_root_node(EditorApi::get_focus_node().as_ref());

                if !EditorApi::is_text_node(root_of_focus_node.as_ref()) && event.key_code() == 13 {
                    event.prevent_default();
                    root_of_focus_node
                        .unwrap()
                        .dyn_into::<HtmlDivElement>()
                        .unwrap()
                        .insert_after(&EditorApi::new_line_element())
                        .clone()
                        .new_focus();
                }
                return false;
            }
        };
        false
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        html! {
            <div id={EditorApi::EDITOR_ID} style={EDITOR_STYLE.clone()}
                class={classes!("padding-small", "padding-small", "pos-abs", "w-100")}
                contenteditable="true"
                oninput={ctx.link().callback(|_| Msg::Input)}
                onmouseup={ctx.link().callback(|_| Msg::MouseUp)}
                onkeydown={ctx.link().callback(Msg::KeyDown)}
            >
                <div><br/></div>
            </div>
        }
    }
}
